/**
 * File: IdentifierMemoryStore.java
 * Created by: mhaimel
 * Created on: 8 Jul 2009
 * CVS:  $Id: IdentifierMemoryStore.java,v 1.2 2009/10/09 15:18:24 mhaimel Exp $
 */
package uk.ac.ebi.curtain.service.impl;

import java.util.Collection;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import uk.ac.ebi.curtain.service.IdentifierStore;
import uk.ac.ebi.curtain.utils.concurrent.UtilsCollect;

/**
 * @author mhaimel
 *
 */
public class IdentifierMemoryStore<I> implements IdentifierStore<I,Integer> {
	private final Object _LOCK_OBJECT = new Object();
	private final AtomicReference<ConcurrentMap<I, Integer>> key2val = 
		new AtomicReference<ConcurrentMap<I, Integer>>(
				UtilsCollect.newConcurrentMap(new ConcurrentHashMap<I, Integer>()));
	private final AtomicInteger idCnt = new AtomicInteger(0);

	public void clear() {
		ConcurrentMap<I, Integer> newValue = UtilsCollect.newConcurrentMap();
		key2val.getAndSet(newValue);		
		idCnt.set(0);
	}

	public Integer getIdentifier(I input) {
		return _getIdentifier(input);
	}

	public boolean hasIdentifier(I input) {
		return _hasIdentifier(input);
	}

	public void registerIdentifier(I key, Integer value) {
		idCnt.incrementAndGet();
		_registerIdentifier(key, value);
	}

	public Iterable<I> getKeys() {
		return _getKeys();
	}
	
	public Integer[] getValues() {
		return _getVaules();
	}
	
	public Integer getOrAddIdentifier(I input) {
		synchronized (_LOCK_OBJECT) {
			Integer identifier = _getIdentifier(input);
			if(null == identifier){
				identifier = _getNextIdentifier();
				_registerIdentifier(input, identifier);
			}
			return identifier;
		}
	}
	

	public void reset(Integer size) {
		key2val.getAndSet(new ConcurrentHashMap<I, Integer>(size));
		idCnt.set(0);
	}
	
	private Integer _getIdentifier(I input) {
		return _getKey2val().get(input);
	}

	private boolean _hasIdentifier(I input) {
		return _getKey2val().containsKey(input);
	}

	private Integer _registerIdentifier(I key, Integer value) {
		return _getKey2val().put(key, value);
	}

	protected ConcurrentMap<I, Integer> _getKey2val() {
		return key2val.get();
	}
	
	private Integer _getNextIdentifier(){
		return idCnt.getAndIncrement();
	}

	@SuppressWarnings("unchecked")
	private Iterable<I> _getKeys() {
		Set<I> set = _getKey2val().keySet();
		return set;
	}

	private Integer[] _getVaules() {
		Collection<Integer> set = _getKey2val().values();
		Integer[] arr = new Integer[set.size()];
		return set.toArray(arr);
	}

	public Integer getSize() {
		return idCnt.intValue();
	}
}
